#include "cmdFunc.h"
#include "54func.h"

// 接收确认信息, 信息正确时返回1, 错误时返回0
int recvMsg(int sockfd, train_t* ptrain) {
    char msgFlag[1] = {0};
    recvn(sockfd, msgFlag, sizeof(msgFlag));
    if(msgFlag[0] == 'e') {
        memset(ptrain, 0, sizeof(train_t));
        recvn(sockfd, &ptrain->length, sizeof(int));
        recvn(sockfd, ptrain->data, ptrain->length);
        printf("Error: %s\n", ptrain->data);
        return 0;
    }
    return 1;
}

// 用小火车发送字符串
int strSend(int netfd, train_t* ptrain, const char* str) {
        memset(ptrain, 0, sizeof(train_t));
        ptrain->length = strlen(str);
        memcpy(ptrain->data, str, ptrain->length);
        send(netfd, &ptrain, ptrain->length + sizeof(int), MSG_NOSIGNAL);
        return 0;
}

// 发送成功信息
int successSend(int netfd) {
    send(netfd, "o", 1, MSG_NOSIGNAL);
    return 0;
}

// 发送错误信息, 如果填入NULL, 发送一个'e'报错信号
// 如果填入字符串, 将其作为报错信息发送给客户端
int errorSend(int netfd, const char* errorMsg) {
    if(errorMsg == NULL) {
        send(netfd, "e", 1, MSG_NOSIGNAL);
    }
    else {
        train_t train;
        strSend(netfd, &train, errorMsg);
    }
    return 0;
}

// cwd栈出栈
static int pop(cwd_t *pcwd) {
    if(pcwd->size == 1) {
        return -1;
    }
    pathToken_t* temp = pcwd->top;
    temp->pnext->pfront = NULL;
    pcwd->top = temp->pnext;
    free(temp);
    pcwd->size--;
    return 0;
}

// cwd栈弹栈
static int push(cwd_t *pcwd,const char *data) {
    pathToken_t* pathToken = (pathToken_t*) calloc(1, sizeof(pathToken_t));
    strcpy(pathToken->data, data);

    pathToken->pnext = pcwd->top;
    pcwd->top->pfront = pathToken;
    pcwd->top = pathToken;
    pcwd->size++;
    return 0;
}

// cwd栈初始化
int cwdInit(const char * username, cwd_t **ppcwd) {
    cwd_t *pcwd = (cwd_t*)calloc(1, sizeof(cwd_t));
    *ppcwd = pcwd;
    pathToken_t* token = (pathToken_t*)calloc(1, sizeof(pathToken_t));
    strcpy(token->data, username);
    pcwd->bottom = token;
    pcwd->top = token;
    pcwd->size = 1;
    return 0;
} 

// 传入cmd_t，解析并执行相应的指令函数,传入cwd_t为指令函数传参
int cmdServerHandler(int netfd, cwd_t **ppcwd, const cmd_t *pcmd) {
    if(*ppcwd == NULL) {
        while(*ppcwd == NULL) {
            switch(pcmd->cmdToken[0]) {
            case'l':{
                        switch(pcmd->cmdToken[3]) {
                        case'o': userLogin(netfd, ppcwd, pcmd);break;
                        case'i': userLogout(netfd, ppcwd, pcmd);break;
                        }
                    } break;
            case'u': userAdd(netfd, pcmd); break;
            }
        }
        return 0;
    }
    cwd_t* pcwd = *ppcwd;
    switch(pcmd->cmdToken[0]) {
    case'c': cdCmd(netfd, pcwd, pcmd); break; 
    case'l': {
                 switch(pcmd->cmdToken[1]) {
                 case's': lsCmd(netfd, pcwd, pcmd); break;
                 case'o': {
                              switch(pcmd->cmdToken[3]) {
                              case'o': userLogin(netfd, ppcwd, pcmd);break;
                              case'i': userLogout(netfd, ppcwd, pcmd);break;
                              }
                          } break;
                 }
             }
    case'p': {
                 switch(pcmd->cmdToken[1]) {
                 case'u':serverGets(netfd, pcwd, pcmd); break;
                 case'w':pwdCmd(netfd, pcwd); break;
                 }
             } break;
    case'u': userAdd(netfd, pcmd); break;
    case'g': serverPuts(netfd, pcwd, pcmd); break;
    case'm': mkdirCmd(netfd, pcwd, pcmd); break;
    case'r': rmCmd(netfd, pcwd, pcmd); break;
    case'q': userLogout(netfd, ppcwd, pcmd);
    }
    return 0;
}

// 清除除用户名目录外的所有目录栈
static int clear(cwd_t *pcwd) {
    pathToken_t* pathToken = pcwd->top;
    while(pcwd->size > 1) {
        pathToken_t* temp = pathToken;
        pathToken = pathToken->pnext;
        free(temp);
    }
    return 0;
}

// 处理当前的token，对cwd进行相应的出栈入栈
static int cwdCtl(cwd_t *pcwd, char* token) {
    if(strcmp(token, "..")) {
        pop(pcwd);
    }
    else if(!strcmp(token, ".")){
        push(pcwd, token);
    }

    return 0;
}

// 判断路径是否和传入类型匹配
// 传入S_IFREG(普通文件)或S_IFDIR(目录)判断类型
// 返回值: 1(匹配), 0(不匹配), -1(无法访问realPath) 
int pathJudge(const char* realPath, mode_t mode) {
    if(access(realPath, W_OK | R_OK)) {
        struct stat fileInfo;
        ssize_t sret = stat(realPath, &fileInfo);
        ERROR_CHECK(sret, -1, "stat");
        if((fileInfo.st_mode & S_IFMT) == mode) {
            return 1;
        }
        else {
            return 0;
        }
    }
    return -1;
}

// 通过传入的虚拟地址，修改cwd栈的结构
static int parsePath(cwd_t *pcwd, char* virtualPath) {
    if(virtualPath[0] == '/') {
        clear(pcwd);
    }
    char* token;
    const char delim[] = "/";
    token = strtok(virtualPath, delim);
    if(token != NULL) {
        cwdCtl(pcwd, token);
    }
    else {
        return 0;
    }
    while((token = strtok(NULL, delim)) != NULL) {
        cwdCtl(pcwd, token);
    }
    return 0;
}

// 获取当前的虚拟地址
int getPath(const cwd_t *pcwd, char *pwdstr) {
    pathToken_t* pathToken = pcwd->bottom;
    while(pathToken != NULL) {
        strcat(pwdstr, pathToken->data);
        pathToken = pathToken->pfront;
        strcat(pwdstr, "/");
    }
    return 0;
}

// pwd指令(遍历栈),pwdstr用于存储遍历后的结果
int pwdCmd(int netfd, const cwd_t *pcwd) {
    char pwdstr[PATH_MAX] = {0};
    getPath(pcwd, pwdstr);
    send(netfd, pwdstr, sizeof(pwdstr), MSG_NOSIGNAL);
    return 0;
}

// cd指令,修改cwd栈,cmd_t获取指令及参数
int cdCmd(int netfd, cwd_t *pcwd, const cmd_t *pcmd) {
    if(pcmd->argc == 0) {
        clear(pcwd);
        successSend(netfd);
    }
    else if(pcmd->argc == 1) {
        char virtualPath[1024] = {0};
        char realPath[PATH_MAX] = {0};
        if(pcmd->argv1[0] == '/') {
            clear(pcwd);
        }
        getPath(pcwd, virtualPath);
        sprintf(realPath, "/%s/%s/%s/", rootDir, virtualPath, pcmd->argv1);
        ssize_t sret;
        if((sret = pathJudge(realPath, S_IFDIR)) == 1) {
            parsePath(pcwd, (char*)pcmd->argv1);
            successSend(netfd);
        }
        else {
            if(sret == 0) {
                errorSend(netfd, "Path type is not a dir.");
            }
            else if(sret == -1) {
                errorSend(netfd, "Path is not valid.");
            }
        }
    }
    return 0;
}

// 将整个目录流中的内容发送给客户端
int sendDir(int netfd, const char* path) {
    struct dirent* entry;
    DIR* dp;

    dp = opendir(path);

    train_t train;
    while((entry = readdir(dp)) != NULL) {
        strSend(netfd, &train, entry->d_name);
    }
    memset(&train, 0, sizeof(train_t));
    send(netfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);
    closedir(dp);
    return 0;
}

// ls指令,cwd_t获取cwd,cmd_t获取指令及参数
int lsCmd(int netfd, const cwd_t *pcwd, const cmd_t *pcmd) {
    char realPath[PATH_MAX] = {0};
    char virtualPath[1024] = {0};
    getPath(pcwd, virtualPath);
    if(pcmd->argc == 0) {
        sprintf(realPath, "/%s/%s/", rootDir, virtualPath);
    }
    else if(pcmd->argc == 1) {
        if(pcmd->argv1[0] == '/') {
            sprintf(realPath, "/%s/%s/%s", rootDir, pcwd->bottom->data, pcmd->argv1);
        }
        else {
            sprintf(realPath, "/%s/%s/%s", rootDir, virtualPath, pcmd->argv1);
        }
    }
    if(access(realPath, W_OK | R_OK)) {
        successSend(netfd);
        sendDir(netfd, realPath);
    }
    else {
        errorSend(netfd, "Path is not valid.");
    }
    return 0;
}

